﻿using Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace IP20.Procdure
{
    // 枚举类型定义（需与C++保持一致）
    public enum FactoryCommunicationApi
    {
        FACTORY_SERIAL_API = 0,
        FACTORY_TCP_CLIENT_API,
        FACTORY_UDP_API,
        FACTORY_EOE_TCP_CLIENT,
        FACTORY_EOE_UDP_API
    }

    public enum FactoryWriteType
    {
        FACTORY_DEBUG_WRITE_TYPE = 0,
        FACTORY_TEST_WRITE_TYPE,
        FACTORY_SAMPLE_WRITE_TYPE,
        FACTORY_PRODUCTION_WRITE_TYPE,
        FACTORY_RETURN_WRITE_TYPE
    }

    public enum FactroyLedColor
    {
        FACTORY_LED_GREEN = 0,
        FACTORY_LED_RED,
        FACTORY_LED_YELLOW,
        FACTORY_LED_BLUE
    }

    public enum FactoryCbType
    {
        FACTORY_INIT_CB_TYPE,        // 初始化操作回调类型
        FACTORY_DEINIT_CB_TYPE,      // 反初始化操作回调类型
        FACTORY_SCAN_CB_TYPE,        // 模块扫描回调类型
        FACTORY_WRITE_CB_TYPE,       // 模块写入回调类型
    }

    public enum FactoryLogLevel
    {
        FACTORY_INFO_LOG = 0,
        FACTORY_ERROR_LOG,
        FACTORY_WARNING_LOG
    }

    public enum FactoryModuleType
    {
        FACTORY_COUPLER_MODULE = 0,
        FACTORY_FORWARD_MODULE,
        FACTORY_SLAVE_MODULE
    }

    // 串口参数结构体
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct SerialApiParam
    {
        public byte port;
        public uint baud;
    }

    // 通讯参数结构体
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct FactoryCommunicationParam
    {
        public SerialApiParam serial;
        public IntPtr tcpClient; // 这里只用串口，其他可忽略
        public IntPtr udpClient;
    }

    // 模块返回数据结构
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct FactoryModuleScanData
    {
        public FactoryModuleType type;
        public byte slotId;
        public byte bpCommAddr;
        public uint id;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public byte[] hwVersion; //模块硬件版本

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public byte[] name; // 模块名称

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public byte[] sn; // 模块序列号

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
        public byte[] mac; // 模块 MAC 地址
    }

    public struct FactoryModuleScanDataConvert
    {
        public FactoryModuleType type;
        public byte slotId;
        public byte bpCommAddr;
        public uint id;

        public string hwVersion; //模块硬件版本

        public string name; // 模块名称

        public string sn; // 模块序列号

        public string mac; // 模块 MAC 地址
    }

    // 回调委托
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void OnfactoryNotify(
        byte commAddr,
        int cbType,
        int status,
        int api,
        // FactoryModuleScanData data,
        // char sn,
        IntPtr data,
        uint datalen);

    public class IP20Factory
    {
        #region import

        // DLL导入
        [DllImport("FactoryApp.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int jq_factory_init(
            FactoryCommunicationApi api,
            ref FactoryCommunicationParam param,
            OnfactoryNotify cb);

        [DllImport("FactoryApp.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int jq_factory_deinit(int contextId, FactoryCommunicationApi api);

        [DllImport("FactoryApp.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int jq_factory_module_scan(int contextId, FactoryCommunicationApi api);

        [DllImport("FactoryApp.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int jq_factory_module_write(int contextId,
            FactoryCommunicationApi api,
            FactoryWriteType type,
            byte[] moduleSn,
            byte commAddr);

        [DllImport("FactoryApp.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int jq_factory_entry_test(int contextId,
            FactoryCommunicationApi api,
            byte commAddr);

        [DllImport("FactoryApp.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int jq_factory_exit_test(int contextId,
          FactoryCommunicationApi api,
          byte commAddr);

        [DllImport("FactoryApp.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int jq_factory_led_ctrl(int contextId,
            FactoryCommunicationApi api,
            byte commAddr,
            byte ledId,
            FactroyLedColor ledColor,
            byte onoff,
            uint id);

        [DllImport("FactoryApp.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int jq_factory_log_write(
            FactoryLogLevel level,
            string tag,
            string log);

        #endregion import

        private int callbackStatus;
        private int retryTimes = 3;
        private GCHandle _notifyDelegateHandle; // 保持委托存活

        private FactoryCommunicationParam commParam;

        private Dictionary<FactoryCbType, ManualResetEvent> notifyEvents;
        public string SN { get; set; }

        public FactoryWriteType WiteType { get; set; }

        public Action<string> ActionNotify;

        public IP20Factory(byte port)
        {
            // 配置串口参数
            var serialParam = new SerialApiParam { port = port, baud = 921600 };
            commParam = new FactoryCommunicationParam
            {
                serial = serialParam,
                tcpClient = IntPtr.Zero,
                udpClient = IntPtr.Zero
            };
            notifyEvents = new Dictionary<FactoryCbType, ManualResetEvent>();
            foreach (FactoryCbType item in Enum.GetValues(typeof(FactoryCbType)))
            {
                notifyEvents.Add(item, new ManualResetEvent(false));
            }
        }

        public void SetPort(byte port)
        {
            // 配置串口参数
            var serialParam = new SerialApiParam { port = port, baud = 921600 };
            commParam = new FactoryCommunicationParam
            {
                serial = serialParam,
                tcpClient = IntPtr.Zero,
                udpClient = IntPtr.Zero
            };
        }

        public IP20Factory()
        {
            notifyEvents = new Dictionary<FactoryCbType, ManualResetEvent>();
            foreach (FactoryCbType item in Enum.GetValues(typeof(FactoryCbType)))
            {
                notifyEvents.Add(item, new ManualResetEvent(false));
            }
        }

        private bool isInit = false;
        private int contextID = 0;

        public void Connect()
        {
            var callback = new OnfactoryNotify(OnfactoryNotifyEvent);
            _notifyDelegateHandle = GCHandle.Alloc(callback); // 防止 GC 回收

            notifyEvents[FactoryCbType.FACTORY_INIT_CB_TYPE] = new ManualResetEvent(false);
            int ret = jq_factory_init(ComApi, ref commParam, callback);
            if (ret != -1)
            {
                contextID = ret;
            }
            else
            {
                throw new Exception($"耦合器初始化失败{ret}");
            }
            Console.WriteLine("模块初始化返回: " + ret);
            notifyEvents[FactoryCbType.FACTORY_INIT_CB_TYPE].WaitOne();
            isInit = true;
        }

        private List<FactoryModuleScanDataConvert> modules;

        private FactoryModuleScanDataConvert FindModule(string name)
        {
            if (modules is null || modules.Count == 0)
            {
                throw new Exception("未扫描到设备");
            }
            var tmp = modules.Where(x => x.name == name).ToArray();
            if (tmp.Count() == 0)
            {
                throw new Exception($"未找到设备{name}");
            }
            return tmp[0];
        }

        public (bool, string, string) ReadSN(string name = "JQ8930E")
        {
            ScanDevice();
            var model = FindModule(name);
            var sn = model.sn;
            if (sn != SN)
            {
                ActionNotify.Invoke($"SN写入失败,反读值={sn}!");
            }
            var mac = model.mac;
            ;
            return (sn == SN, sn, mac);
        }

        public void WriteSN(string name = "JQ8930E")
        {
            if (SN is null || SN.Length != 15)
            {
                throw new ArgumentException("SN长度异常");
            }
            byte comAddr = FindModule(name).bpCommAddr;
            // 模块写入
            //string sn = "AZ5705PC7400135";
            byte[] snBytes = System.Text.Encoding.UTF8.GetBytes(SN);

            notifyEvents[FactoryCbType.FACTORY_WRITE_CB_TYPE] = new ManualResetEvent(false);

            var ret = jq_factory_module_write(contextID, ComApi, WiteType, snBytes, comAddr);
            CheckResult(ret);
            Console.WriteLine("模块反初始化返回: " + ret);
            notifyEvents[FactoryCbType.FACTORY_WRITE_CB_TYPE].WaitOne();

            switch (callbackStatus)
            {
                case 0:
                case 15:
                    //pass;
                    break;

                case 16:
                    // mac 地址余量不足
                    Global.MacFull = false;
                    break;

                default:
                    throw new Exception(StatusMessage[callbackStatus]);
            }
        }

        private bool CheckResult(int ret)
        {
            if (ret != 0)
            {
                ActionNotify?.Invoke($"耦合器通信返回{ret}!");
                //throw new Exception();
            }
            return ret == 0;
        }

        public void SetLed(string name, byte ledIndex, FactroyLedColor color, byte offOn)
        {
            // 控制LED
            var module = FindModule(name);
            byte comAddr = module.bpCommAddr;
            uint id = module.id;

            var ret = jq_factory_led_ctrl(contextID, ComApi, comAddr, ledIndex, color, offOn, id);
            CheckResult(ret);
            Console.WriteLine("LED控制返回: " + ret);
        }

        public FactoryCommunicationApi ComApi { get; set; }

        public void EnterTestMode(string name)
        {
            // 进入测试模式
            var module = FindModule(name);
            byte comAddr = module.bpCommAddr;
            var ret = jq_factory_entry_test(contextID, ComApi, comAddr);
            CheckResult(ret);
            Console.WriteLine("进入测试模式返回: " + ret);
        }

        public void ExitTestMode(string name)
        {
            // 进入测试模式
            var module = FindModule(name);
            byte comAddr = module.bpCommAddr;
            var ret = jq_factory_exit_test(contextID, ComApi, comAddr);
            CheckResult(ret);
            Console.WriteLine("退出测试模式返回: " + ret);
        }

        public void Close()
        {
            if (!isInit)
            {
                return;
            }
            notifyEvents[FactoryCbType.FACTORY_DEINIT_CB_TYPE] = new ManualResetEvent(false);
            var ret = jq_factory_deinit(contextID, ComApi);
            CheckResult(ret);
            Console.WriteLine("模块反初始化返回: " + ret);
            notifyEvents[FactoryCbType.FACTORY_DEINIT_CB_TYPE].WaitOne();
            isInit = false;
        }

        public void ScanDevice()
        {
            // 模块扫描
            for (int i = 0; i < retryTimes; i++)
            {
                notifyEvents[FactoryCbType.FACTORY_SCAN_CB_TYPE] = new ManualResetEvent(false);
                var ret = jq_factory_module_scan(contextID, ComApi);
                CheckResult(ret);
                Console.WriteLine("模块扫描返回: " + ret);
                notifyEvents[FactoryCbType.FACTORY_SCAN_CB_TYPE].WaitOne();
                bool checkResult = true;
                foreach (var item in modules)
                {
                    if (!CheckModule(item))
                    {
                        checkResult = false;
                    }
                }
                if (checkResult && ret == 0)
                {
                    return;
                }
            }
            throw new Exception("模块扫描返回信息异常");
        }

        /// <summary>
        /// 检查模块扫描结果
        /// </summary>
        /// <param name="factory"></param>
        /// <returns></returns>

        private bool CheckModule(FactoryModuleScanDataConvert factory)
        {
            if (string.IsNullOrEmpty(factory.name))
            {
                return false;
            }
            //if (string.IsNullOrEmpty(factory.sn))
            //{
            //    return false;
            //}
            if (string.IsNullOrEmpty(factory.mac))
            {
                return false;
            }
            if (string.IsNullOrEmpty(factory.hwVersion))
            {
                return false;
            }
            //if (factory.sn.Length != 15)
            //{
            //    return false;
            //}
            if (factory.mac.Length != 17)
            {
                return false;
            }
            return true;
        }

        private Dictionary<int, string> StatusMessage = new Dictionary<int, string>()
        {
            { 0, "操作成功"},
            { 1,"处理中"},
            { 2,"初始化失败"},
            { 3,"反初始化失败"},
            { 4,"尚未初始化"},
            { 5,"通信失败"},
            { 6,"模块扫描失败"},
            { 7,"写入失败 "},
            { 8,"日志写入失败"},
            { 9,"其他未知失败"},
            { 10,"MAC地址写入失败"},
            { 11,"权限写入失败"},
            { 12,"序列号写入失败"},
            { 13,"序列号字符串写入失败"},
            { 14,"写入加密失败"},
            { 15,"序列号已存在"},
            { 16,"MAC可用量报警"},
        };

        private void OnfactoryNotifyEvent(byte commAddr, int cbType, int status, int api, IntPtr data, uint datalen)
        {
            FactoryCbType factoryCbType = (FactoryCbType)cbType;
            callbackStatus = status;
            Mylog.Info($"status的值:{status}");
            ActionNotify?.Invoke($"[回调] commAddr={commAddr}, cbType={Enum.GetName(typeof(FactoryCbType), factoryCbType)}, status={StatusMessage[status]}, api={api} ,datalen={datalen}");
            switch (factoryCbType)
            {
                case FactoryCbType.FACTORY_INIT_CB_TYPE:

                    break;

                case FactoryCbType.FACTORY_DEINIT_CB_TYPE:
                    break;

                case FactoryCbType.FACTORY_SCAN_CB_TYPE:
                    modules = new List<FactoryModuleScanDataConvert>();
                    {
                        int factoryModuleSize = Marshal.SizeOf(typeof(FactoryModuleScanData));
                        int numModules = (int)(datalen / factoryModuleSize);
                        for (int i = 0; i < numModules; i++)
                        {
                            IntPtr modulePtr = IntPtr.Add(data, i * factoryModuleSize);
                            FactoryModuleScanData scanedModule = Marshal.PtrToStructure<FactoryModuleScanData>(modulePtr);
                            string sn = System.Text.Encoding.UTF8.GetString(scanedModule.sn).TrimEnd('\0');
                            string mac = BitConverter.ToString(scanedModule.mac).Replace("-", ":");
                            string name = System.Text.Encoding.UTF8.GetString(scanedModule.name).TrimEnd('\0');
                            string hwVersion = System.Text.Encoding.UTF8.GetString(scanedModule.hwVersion).TrimEnd('\0');

                            FactoryModuleScanDataConvert module;
                            module.type = scanedModule.type;
                            module.slotId = scanedModule.slotId;
                            module.bpCommAddr = scanedModule.bpCommAddr;
                            module.id = scanedModule.id;
                            module.hwVersion = hwVersion;
                            module.name = name;
                            module.sn = sn;
                            module.mac = mac;
                            modules.Add(module);

                            ActionNotify?.Invoke($"模块{i + 1}:NAME={name} SN = {sn},MAC = {mac},SLotId = {scanedModule.slotId},BPCommAddr = {scanedModule.bpCommAddr},ID = {scanedModule.id},ModuleType = {scanedModule.type} hwVersion={hwVersion}");
                        }
                    }
                    break;

                case FactoryCbType.FACTORY_WRITE_CB_TYPE:
                    break;

                default:
                    break;
            }
            notifyEvents[factoryCbType]?.Set();
        }
    }
}